#pragma once
#include <Rz/IObject.h>
#include <Rz/EnumFlags.h>
#include <Rz/Graphics/Math/Size.h>
#include <Rz/Graphics/Math/Rectangle.h>
#include <Rz/Graphics/Math/Vector3.h>
#include <Rz/Graphics/Math/Vector4.h>
#include <Rz/Collections/List.h>

#include "IRenderTarget2D.h"
#include "IVertexBuffer.h"
#include "IIndexBuffer.h"
#include "IVertexArray.h"
#include "IEffect.h"
#include "Geometry.h"
#include "GBuffer.h"
#include "IRasterizerState.h"
#include "Viewport.h"

#include <initializer_list>



// RZ_CLASS_FWD_DECL3(Rz, Graphics, Render, IRenderTarget2D);
// RZ_CLASS_FWD_DECL3(Rz, Graphics, Render, IVertexBuffer);
// RZ_CLASS_FWD_DECL3(Rz, Graphics, Render, IIndexBuffer);
// RZ_CLASS_FWD_DECL3(Rz, Graphics, Render, IVertexArray);
// RZ_CLASS_FWD_DECL3(Rz, Graphics, Render, IEffect);
// RZ_CLASS_FWD_DECL3(Rz, Graphics, Render, Geometry);
// RZ_CLASS_FWD_DECL3(Rz, Graphics, Render, GBuffer);
// RZ_CLASS_FWD_DECL3(Rz, Graphics, Render, IRasterizerState);
// RZ_CLASS_FWD_DECL3(Rz, Graphics, Render, Viewport);
// 
RZ_ENUM_FWD_DECL3(Rz, Graphics, Render, SurfaceFormat);
RZ_ENUM_FWD_DECL3(Rz, Graphics, Render, DepthFormat);
RZ_ENUM_FWD_DECL3(Rz, Graphics, Render, RenderTargetUsage);

namespace Rz { namespace Graphics { namespace Render {

enum class PrimitiveType
{
	TriangleList,
	TriangleStrip,
	LineList,
	LineStrip,
};

enum class ClearOptions
{
	Target = 1,
	DepthBuffer = 2,
	Stencil = 4        
};

RZ_MARK_AS_ENUMFLAGS(ClearOptions);

class IRenderDevice :
	virtual public IObject
{
	public:
		RZ_RTTI_INTERFACE(Rz::Graphics::Render, IRenderDevice);
		RZ_RTTI_IMPLEMENTS(IObject);
		RZ_RTTI_END;

		virtual bool Init() = 0;
		virtual void End() = 0;

		virtual void Clear(ClearOptions clearMode, const Math::Vector4& color) = 0;
		virtual void Clear(const Math::Vector4& color) { Clear(ClearOptions::Target | ClearOptions::DepthBuffer /* stencil ? */, color); }

		virtual IRenderTarget2D* CreateRenderTarget2D(const Math::Size& size) = 0;
		virtual IRenderTarget2D* CreateRenderTarget2D(const Math::Size& size, SurfaceFormat surfaceFormat) = 0;
		virtual IRenderTarget2D* CreateRenderTarget2D(const Math::Size& size, SurfaceFormat surfaceFormat, DepthFormat depthFormat) = 0;
		virtual IRenderTarget2D* CreateRenderTarget2D(const Math::Size& size, SurfaceFormat surfaceFormat, DepthFormat depthFormat, RenderTargetUsage usage) = 0;

		virtual IVertexBuffer* CreateVertexBuffer() = 0;
		virtual IIndexBuffer* CreateIndexBuffer() = 0;
		virtual IVertexArray* CreateVertexArray() = 0;
		virtual IEffect* CreateEffect() = 0;
		virtual IEffect* CreateEffect(const String& shader) = 0;

		virtual Geometry* CreateGeometry() = 0;

		virtual Viewport* CreateViewport(const Math::Rectangle& bounds) = 0;

		virtual GBuffer* CreateGBuffer(const Math::Size& size) = 0;

		//////////////////////////////////////////////////////////////////////////

		virtual void SetRenderTarget(IRenderTarget2D* renderTarget);
		virtual void SetRenderTargets(const Collections::List<Ptr<IRenderTarget2D>>& renderTargets);
		virtual void SetRenderTargets(std::initializer_list<IRenderTarget2D*> renderTargets);

		virtual const Collections::List<Ptr<IRenderTarget2D>>& GetRenderTargets() const { return _renderTargets; }

		virtual void SetRasterizerState(IRasterizerState* rasterizerState) { _rasterizerState = rasterizerState; }
		virtual void DrawIndexedPrimitives(PrimitiveType primitiveType, IVertexArray* vertexArray) = 0;

	private:
		Collections::List<Ptr<IRenderTarget2D>> _renderTargets;
		Ptr<IRasterizerState> _rasterizerState;
};

} } }
